package com.jxpanda.starter.config.security.service;

import com.jxpanda.starter.config.security.authentication.TokenAuthentication;
import com.jxpanda.starter.config.security.token.AbstractTokenHelper;
import com.jxpanda.starter.config.security.token.Token;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.password.PasswordEncoder;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;

/**
 * @author Panda
 */
public abstract class AbstractAuthService<T> implements ReactiveUserDetailsService {

    @Resource
    protected PasswordEncoder passwordEncoder;

    @Resource
    protected ReactiveAuthenticationManager authenticationManager;

    @Resource
    protected AbstractTokenHelper tokenHelper;

    /**
     * 构建token数据
     *
     * @param user 传入的用户对象
     * @return 签名后的Token，目前是一个jwt
     */
    protected abstract Mono<Token> buildToken(T user);

    /**
     * 使用用户账号查询用户对象
     *
     * @param account 用户账号，基于业务可以是邮箱/手机号等
     * @return 用户对象
     */
    protected abstract Mono<T> selectUserByAccount(String account);

    @Override
    public Mono<UserDetails> findByUsername(String username) {
        return selectUserByAccount(username)
                .flatMap(this::buildToken);
    }

    /**
     * 使用用户对象直接登录
     */
    public Mono<Token> login(T user) {
        return buildToken(user)
                .flatMap(token-> authenticationManager.authenticate(new TokenAuthentication(token))
                        .map(authentication -> tokenHelper.signature(((TokenAuthentication) authentication).getToken())));
    }

    /**
     * 用户登录
     * 登录成功后，返回UserDetail
     */
    public Mono<Token> login(String account, String password) {
        // 使用spring-security校验数据（登录）
        return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(account, password))
                .map(Authentication::getPrincipal)
                .cast(Token.class)
                .map(token -> tokenHelper.signature(token));

    }

    public boolean isPasswordMatch(String password, String encodedPassword) {
        return passwordEncoder.matches(password, encodedPassword);
    }

}
